#pragma once
#include "cli.hpp"
#include <filesystem>
#include <iostream>
#include <map>
#include <set>
#include <string>
#include <vector>

// 数据结构相关的定义
extern const std::filesystem::path GIT_DIR;
extern const std::filesystem::path OBJECT_DIR;
extern const std::filesystem::path current_dir;
extern const std::filesystem::path HEADS_DIR;
extern const std::filesystem::path HEAD;
extern const std::filesystem::path REFS_DIR;
extern const std::filesystem::path TAGS_DIR;
extern const std::filesystem::path INDEX_FILE;
extern const std::string DEFAULT_PARENT_OID;
#define OBEJECT_TYPE_LENGTH 1
extern const char *SEPERATOR;
// 文件的类型 添加到blob中
enum ObjectType { INVAILD_OBJECT = 0, BLOB_OBJ = 1, TREE_OBJ, COMMIT_MESSAGE, TAG_OBJ };

// 文件的类型数组
struct ObjectFile {
    ObjectType type;
    std::string oid;
    std::string file_name; // 用绝对路径表示文件名
    std::string to_string() {
        switch (type) {
        case BLOB_OBJ:
        case TREE_OBJ:
            return std::string(1, type + '0') + " " + oid + " " + file_name + "\n";
        default:
            return std::string("error object type");
        }
    };
    // 重载输入操作符 >>
    friend std::istream &operator>>(std::istream &input, ObjectFile &obj) {
        // 读取整行数据

        std::string oid_str;
        std::string file_name_str;
        // 从字符串流中解析数据
        if (input >> oid_str >> file_name_str) {
            // 设置ObjectFile对象的成员变量
            obj.oid       = oid_str;
            obj.file_name = file_name_str;
        } else {
            // 如果解析失败，标记输入流的错误状态
            std::cerr << "Error: parse object file failed" << std::endl;
            input.setstate(std::ios::failbit);
        }
        return input;
    }
};

// commit message对象
struct CommitMessage {
    std::string branch_name;
    std::vector<std::string> parent_oid_vct;
    std::string obj_oid;         // 指向存储object的oid
    std::string add_message_oid; // 指向commit message的oid
    std::tm commit_local_time;
    std::string message;

    std::string to_string() {
        std::stringstream ss;
        ss << "branch " << branch_name << "\n";
        ss << "tree " << obj_oid << "\n";
        for (auto &parent_oid : parent_oid_vct) {
            ss << "parent " << parent_oid << "\n";
        }
        ss << "Time " << std::put_time(&commit_local_time, "%Y-%m-%d %H:%M:%S") << "\n";
        ss << "add commit oid " << add_message_oid << "\n";
        ss << "commit message " << message << "\n";
        return ss.str();
    }
    // 重载输入操作符 >>
    friend std::istream &operator>>(std::istream &input, CommitMessage &obj) {
        std::string line;

        // 读取 branch 行
        if (std::getline(input, line)) {
            if (line.rfind("branch ", 0) == 0) {
                obj.branch_name = line.substr(7); // 获取 branch 后面的内容
            }
        }

        // 读取 tree 行
        if (std::getline(input, line)) {
            if (line.rfind("tree ", 0) == 0) {
                obj.obj_oid = line.substr(5); // 获取 tree 后面的内容
            }
        }

        // 读取 parent 行
        while (std::getline(input, line)) {
            if (line.rfind("parent ", 0) == 0) {
                obj.parent_oid_vct.push_back(line.substr(7)); // 获取 parent 后面的内容
            } else {
                break;
            }
        }

        // 读取 Time 行

        if (line.rfind("Time ", 0) == 0) {
            std::istringstream time_stream(line.substr(5));
            std::string time_str;
            std::getline(time_stream, time_str);
            std::istringstream ss(time_str);
            ss >> std::get_time(&obj.commit_local_time, "%Y-%m-%d %H:%M:%S");
        }
        // 读取 commit oid 行
        if (std::getline(input, line)) {
            if (line.rfind("add commit oid ", 0) == 0) {
                obj.add_message_oid = line.substr(15); // 获取 commit oid 后面的内容
            }
        }

        // 读取 commit message 行
        if (std::getline(input, line)) {
            if (line.rfind("commit message ", 0) == 0) {
                obj.message = line.substr(15); // 获取 commit message 后面的内容
            }
        }

        return input;
    }
};

struct AddMessage {
    std::string file_name;
    std::string obj_oid; // 指向存储object的oid
};

bool check_file_exist(const std::string &fileName, bool isDir = false);

ErrorMessage file_SHA1(const std::string &fileName);
ErrorMessage string_SHA1(std::vector<std::string> &str);
// 把文件夹转换成树对象，返回oid
ErrorMessage convert_dir_to_tree(std::filesystem::path target_dir = current_dir);
// 把文件转换成blob对象，返回oid
ErrorMessage convert_files_to_blob(const std::string &file_path);

// 把blob对象内容写入到输出流中
ErrorMessage convert_blob_to_stream(std::string &oid, std::ostream &out);
// 把树对象转换成文件夹，并且将原来文件夹里的文件还原
ErrorMessage convert_tree_to_dir(std::string &oid);
// 删除directory目录下除了exceptFolder以外的文件
void delete_files_except(const std::filesystem::path &directory,
                         const std::set<std::filesystem::path> &exceptPaths);

// 获取文件流中的文件类型
ObjectType get_file_type(std::istream &input);

std::string get_tag_oid(std::string tag_name);


std::map<std::string, ObjectFile> get_object_list(std::filesystem::path target_dir);
std::vector<ObjectFile> convert_object_list(std::filesystem::path target_dir);